package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.enums.LessonStatus;
import com.tianji.learning.domain.enums.PlanStatus;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author xie
 * @since 2024-11-06
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    //远程调用课程微服务，作用：通过课程id查询课程的信息

    private final CourseClient courseClient;

    private final CatalogueClient catalogueClient;


    private final LearningRecordMapper recordMapper;



    /**
     * MQ方法：支付后添加课程
     *
     * @param courseIds
     * @param userId
     */
    @Override
    @Transactional
    public void addLearningLesson(List<Long> courseIds, Long userId) {

        //通过课程id查询课程信息
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);

        //判断是否查到课程信息，没查到则返回
        if (ObjectUtil.isEmpty(simpleInfoList)) {
            log.info("课程消息不存在，无法将课程添加到课程表");
            return;
        }

        //由于传过来的课程id是list类型，有多个课程，因此，每一条课程都需要有一条课程表记录，此处创建课程表的集合
        List<LearningLesson> learningLessons = new ArrayList<>(simpleInfoList.size());
        //遍历课程
        for (CourseSimpleInfoDTO courseSimpleInfoDTO : simpleInfoList) {

            //创建一个空的课程表实体类，用来存放每一条课程对应的课程表记录
            LearningLesson learningLesson = new LearningLesson();
            //课程表对应的用户字段
            learningLesson.setUserId(userId);
            //课程表对应的课程id字段，从远程调用的课程表中查询获取
            learningLesson.setCourseId(courseSimpleInfoDTO.getId());

            //课程过期时间：从远程调用的课程表中拿
            Integer validDuration = courseSimpleInfoDTO.getValidDuration();
            //如果过期字段为空，或者小于等于0，则说明该课程没有过期时间，跳过此条赋值
            if (ObjectUtil.isEmpty(validDuration) || validDuration <= 0) {
                continue;
            }
            //获取过期时间，并加上当前月份的时间得到过期时间，赋值给课程表中课程的过期时间
            learningLesson.setExpireTime(LocalDateTime.now().plusMonths(validDuration));
            //将此条构建好的课程表记录添加到集合中
            learningLessons.add(learningLesson);

        }
        //将课程表集合中的数据一次性添加到数据库
        saveBatch(learningLessons);

    }

    /**
     * 1、分页查询我的课表（Day2 - 课堂1）
     *
     * @param pageQuery
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> selectLearningLessonPage(PageQuery pageQuery) {

        //用户id
        Long userid = UserContext.getUser();
        //获取分页并按字段排序后的课程表Page对象。
        Page<LearningLesson> page = lambdaQuery()
                .eq(LearningLesson::getUserId, userid)
                .page(pageQuery.toMpPage("latest_learn_time", false));

        //从page中获取结果
        List<LearningLesson> records = page.getRecords();
        //判断当前页内容是否为空
        if (ObjectUtil.isEmpty(records)) {
            return PageDTO.empty(page);
        }


        //传入课表集合，返回得到课程的map
        Map<Long, CourseSimpleInfoDTO> courseMap = getLongCourseSimpleInfoDTO2Map(records);

        //构建vos供return返回给前端
        List<LearningLessonVO> learningLessonVOS = new ArrayList<>(records.size());
        //遍历课程表
        for (LearningLesson record : records) {
            //将每一个课程表转为Vo的课程表（区别在于，vo多了三个字段，分别是课程的三个字段，课程封面，课程名，课程章节）
            LearningLessonVO learningLessonVO = BeanUtils.toBean(record, LearningLessonVO.class);

            //传入当前需要组装的的课程表的课程id，获取课程的map
            CourseSimpleInfoDTO courseSimpleInfoDTO = courseMap.get(record.getCourseId());
            //将课程的三个字段赋值给课程表，完成组装
            learningLessonVO.setCourseName(courseSimpleInfoDTO.getName());
            learningLessonVO.setCourseCoverUrl(courseSimpleInfoDTO.getCoverUrl());
            learningLessonVO.setSections(courseSimpleInfoDTO.getSectionNum());

            //将完成课程字段组装后的vo添加到vos集合中
            learningLessonVOS.add(learningLessonVO);
        }
        //返回给前端
        return PageDTO.of(page, learningLessonVOS);

//        //
//        Set<Long> collect = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
//        //所有课程
//        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(collect);
//
//        if (ObjectUtil.isEmpty(simpleInfoList)){
//            throw new BizIllegalException("课程为空");
//        }
//
//        List<LearningLessonVO> learningLessonsVo =new ArrayList<>(records.size());
//
//        for (LearningLesson record : records) {
//
//            LearningLessonVO learningLessonVO = BeanUtils.toBean(record, LearningLessonVO.class);
//
//            for (CourseSimpleInfoDTO courseSimpleInfoDTO : simpleInfoList) {
//                if (courseSimpleInfoDTO.getId().equals(learningLessonVO.getCourseId())){
//                    learningLessonVO.setCourseName(courseSimpleInfoDTO.getName());
//                    learningLessonVO.setCourseCoverUrl(courseSimpleInfoDTO.getCoverUrl());
//                    learningLessonVO.setSections(courseSimpleInfoDTO.getSectionNum());
//                    break;
//                }
//            }
//
//            learningLessonsVo.add(learningLessonVO);
//        }

    }

    /**
     * 2、查询我正在学习的课程 (Day2- 作业1)
     *
     * @return
     */
    @Override
    public LearningLessonVO selectLessonsNow() {
        // 1.获取当前登录的用户
        Long userId = UserContext.getUser();

        //查了课表，并返回课表的数据
        LearningLesson learningLesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();

        if (learningLesson == null) {
            return null;
        }

        //需要课程的三个字段，所以课表实体转换为vo
        LearningLessonVO learningLessonVO = BeanUtils.toBean(learningLesson, LearningLessonVO.class);

        //由于需要课程的三个字段，此处远程调用课程接口
        CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(learningLessonVO.getCourseId(), false, false);
        learningLessonVO.setCourseName(courseInfo.getName());
        learningLessonVO.setCourseCoverUrl(courseInfo.getCoverUrl());
        learningLessonVO.setSections(courseInfo.getSectionNum());


        //查询用户的总课程数
        Integer count = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .count();


        //远程获取课程目录信息
        List<CataSimpleInfoDTO> cataInfos =
                catalogueClient.batchQueryCatalogue(CollUtils.singletonList(learningLesson.getLatestSectionId()));

        if (!ObjectUtil.isEmpty(cataInfos)) {
            CataSimpleInfoDTO cataSimpleInfoDTO = cataInfos.get(0);
            //小节名
            learningLessonVO.setLatestSectionName(cataSimpleInfoDTO.getName());
            //小节编号
            learningLessonVO.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex());
        }

        //课程总数
        learningLessonVO.setCourseAmount(count);


        //返回vo
        return learningLessonVO;
    }


    /**
     * 3、查询指定课程信息 (Day2- 作业2)
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO selectCourseId(Long courseId) {

        Long user = UserContext.getUser();
        LearningLesson learningLesson = getOne(buildUserIdAndCourseIdWrapper(user, courseId));
        if (learningLesson == null) {
            return null;
        }
        return BeanUtils.toBean(learningLesson, LearningLessonVO.class);


    }


    /**
     * 4、删除指定课程信息（Day2 - 作业3）
     *
     * @param userId
     * @param courseId
     */
    @Override
    public void deleteCourseFromLesson(Long userId, Long courseId) {
        // 1.获取当前登录用户
        if (userId == null) {
            userId = UserContext.getUser();
        }
        // 2.删除课程
        remove(buildUserIdAndCourseIdWrapper(userId, courseId));
    }


    /**
     * 5、统计课程学习人数 (Day2- 作业4)
     *
     * @return
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {

        // select count(1) from xx where course_id = #{cc} AND status in (0, 1, 2)
        return lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus,
                        LessonStatus.NOT_BEGIN.getValue(),
                        LessonStatus.LEARNING.getValue(),
                        LessonStatus.FINISHED.getValue())
                .count();

    }

    /**
     * 创建或修改学习计划
     *
     * @param learningPlanDTO
     */
    @Override
    public void createPlans(LearningPlanDTO learningPlanDTO) {
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(UserContext.getUser(), learningPlanDTO.getCourseId()));

        this.lambdaUpdate()
                .set(LearningLesson::getWeekFreq, learningPlanDTO.getFreq())
                .set(lesson.getPlanStatus() == PlanStatus.NO_PLAN, LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .update();

    }

    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery pageQuery) {
        LearningPlanPageVO vo = new LearningPlanPageVO();
        // 1、获取当前登录用户
        Long userId = UserContext.getUser();

        // 2、分页查询我的课表（我的、创建过学习计划、状态是未学习、学习中，根据最近学习时间倒序排序）
        Page<LearningLesson> pageResult = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = pageResult.getRecords();
        if(ObjectUtil.isEmpty(records)){
            return vo.emptyPage(pageResult);
        }

        // 3、查询课程填充数据（根据课程ID集合查询课程信息）
        Map<Long, CourseSimpleInfoDTO> courseInfoMap = getLongCourseSimpleInfoDTO2Map(records);

        // 4、统计学习记录表中本周学习小节数据
        //select r.`lesson_id`as id,count(1) as num from learning_record r where r.`user_id` = 2 and r.`finished` = 1
        //and r.`finish_time` between '2024-11-04 00:00:00' and '2024-11-10 23:59:59'
        //group by r.`lesson_id`
        // 4.1、获取本周开始、结束日期
        LocalDateTime begin = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime end = DateUtils.getWeekEndTime(LocalDate.now());
        QueryWrapper<LearningRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("`lesson_id`as id,count(1) as num")
                .eq("user_id",userId)
                .eq("finished",true)
                .between("finish_time",begin,end)
                .groupBy("lesson_id");

        List<Map<String, Object>> mapList = recordMapper.selectMaps(queryWrapper);
        Map<Long, Long> learnedSectionNumMap = mapList.stream()
                .collect(Collectors.toMap(r -> (Long) r.get("id"), r -> (Long) r.get("num")));

        // 5、循环之前查询的分页课表数据，进行数据填充
        List<LearningPlanVO> voList = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            // 5.1、拷贝po至vo
            LearningPlanVO learningPlanVO = BeanUtils.copyBean(record, LearningPlanVO.class);

            // 5.2、填充课程数据
            CourseSimpleInfoDTO couseInfo = courseInfoMap.get(record.getCourseId());
            if(ObjectUtil.isNotEmpty(couseInfo)){
                learningPlanVO.setCourseName(couseInfo.getName());
                learningPlanVO.setSections(couseInfo.getSectionNum());
            }

            // 5.3、填充本周已学习小节数
            learningPlanVO.setWeekLearnedSections(learnedSectionNumMap.getOrDefault(record.getId(),0L).intValue());

            // 5.4、添加vo至list
            voList.add(learningPlanVO);
        }

        // 6、计算本周计划学习的总小节数
        //select sum(week_freq) from learning_lesson where user_id = 2 and plan_status = 1
        //and status in (0,1)
        LearningLesson lesson = this.query()
                .select("sum(week_freq) as total_weekfreq")
                .eq("user_id", userId)
                .eq("plan_status", PlanStatus.PLAN_RUNNING)
                .in("status", LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .one();

        // 7、计算本周已学习的总小节数
        long sum = learnedSectionNumMap.values().stream().mapToLong(num -> num.longValue()).sum();
        Integer weekFinished = Long.valueOf(sum).intValue();

        /*long sum1 = learnedSectionNumMap.values().stream().flatMapToLong(num -> LongStream.of(num)).sum();
        Long sum2 = learnedSectionNumMap.values().stream().map(Function.identity()).reduce(Long::sum).get();
        Long sum3 = learnedSectionNumMap.values().stream().collect(Collectors.summingLong(num -> num));
        long sum4 = learnedSectionNumMap.values().stream().mapToLong(num -> num).summaryStatistics().getSum();*/

        // 8、封装Vo并返回
        vo.setWeekTotalPlan(lesson.getTotalWeekfreq());
        vo.setWeekFinished(weekFinished);
        vo.setWeekPoints(null); // TODO 本周学习积分
        return vo.pageInfo(pageResult.getTotal(),pageResult.getPages(),voList);
    }


    /**
     * 6、校验当前课程是否已经报名 (Day2- 作业5)
     *
     * @return
     */
    @Override
    public Long isLessonValid(Long courseId) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        if (userId == null) {
            return null;
        }
        // 2.查询课程
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
        if (lesson == null || LessonStatus.EXPIRED.equals(lesson.getStatus())) {
            return null;
        }
        return lesson.getId();
    }


    /**
     * 传入课表集合，获取课程的map(通过课表集合获取所有对应的课程)
     */
    private Map<Long, CourseSimpleInfoDTO> getLongCourseSimpleInfoDTO2Map(List<LearningLesson> records) {

        //收集所有课程表的课程id
        Set<Long> collect = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        //通过课程id调用课程微服务远程，获取所有的课程信息（包括课程封面、课程名、课程章节）
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(collect);

        //判空
        if (ObjectUtil.isEmpty(simpleInfoList)) {
            throw new BizIllegalException("没有找到课程");
        }

        //将课程的id作为键，课程本身作为value，收集成一个map
        Map<Long, CourseSimpleInfoDTO> courseMap = simpleInfoList.stream().
                collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));
        //返回这个map
        return courseMap;
    }


    /**
     * 根据用户id和课程id获取对应课表的信息
     *
     * @param userId
     * @param courseId
     * @return
     */
    private LambdaQueryWrapper<LearningLesson> buildUserIdAndCourseIdWrapper(Long userId, Long courseId) {
        LambdaQueryWrapper<LearningLesson> queryWrapper = Wrappers.<LearningLesson>lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        return queryWrapper;
    }

}

























